
#include "LED.h"
#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include <inttypes.h>

void ExitIAP(void);

#define USARTy USART1
#define USARTy_GPIO GPIOA
#define USARTy_CLK RCC_APB2Periph_USART1
#define USARTy_GPIO_CLK RCC_APB2Periph_GPIOA
#define USARTy_RxPin GPIO_Pin_10
#define USARTy_TxPin GPIO_Pin_9
#define USARTy_IRQn USART1_IRQn
#define USARTy_IRQHandler USART1_IRQHandler
void DMA_Configuration(void);

/* Define Application Parameters */
#define FLASH_START_ADDR ((uint32_t)0x08000000)
#define BootloadSize 0x8000
#define VectorOffset BootloadSize
#define ApplicationAddress ((uint32_t)(FLASH_START_ADDR + BootloadSize))
typedef void (*pFunction)(void);
pFunction LoadApplication;
uint32_t JumpAddress;
uint16_t TimeSec = 0;

void Programng(uint16_t Offset, uint16_t SWSIZE, const uint8_t *DataBuff);
#define FLASH_PAGE_SIZE ((uint16_t)0x800) // 2k 大容量
#define BANK1_WRITE_START_ADDR ((uint32_t)ApplicationAddress)

volatile uint8_t FLASHStatus = 0;
volatile uint8_t FLASH_Offset = 0;

const char *banner =
    "\n=========================================================="
    "\n**Welcome to Mili Bootloader For STM32**"
    "\n  Version 1.0\n  Email:leonard_tech@aliyun.com\n  2017/12/21"
    "\n===========================================================\n [1]For "
    "Update SW\n [2]Run UserApp\n[*]>";
#define RX_BUF_SIZE ((uint16_t)0x800) // 2k
volatile uint8_t CMD_FLAG = 0;
uint16_t RXi;
char RXc;
char RX_BUF[RX_BUF_SIZE] = {'\0'};
char FwBuff[RX_BUF_SIZE] = {'\0'};

void *memcpy(void *dst, const void *src, unsigned n) {
  const char *p = src;
  char *q = dst;

  while (n--) {
    *q++ = *p++;
  }
  return dst;
}
void LoadApp(void) {
  /* If Program has been written */
  if (((*(__IO uint32_t *)ApplicationAddress) & 0x2FFE0000) == 0x20000000) {
    /* Set system control register SCR->VTOR  */
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, VectorOffset);
    JumpAddress = *(__IO uint32_t *)(ApplicationAddress + 4);
    LoadApplication = (pFunction)JumpAddress;
    __set_MSP(*(__IO uint32_t *)ApplicationAddress);
    ExitIAP();
    LoadApplication();
  }
}

void clear_RXBuffer(void) {
  for (RXi = 0; RXi < RX_BUF_SIZE; RXi++)
    RX_BUF[RXi] = '\0';
  RXi = 0;
}
void USARTSend(const char *pucBuffer) {
  while (*pucBuffer) {
    USART_SendData(USARTy, *pucBuffer++);
    while (USART_GetFlagStatus(USARTy, USART_FLAG_TC) == RESET) {
    }
  }
}

void usart_init(void) {
  /* Enable USARTy and USARTy_GPIO clock */
  RCC_APB2PeriphClockCmd(USARTy_CLK | USARTy_GPIO_CLK, ENABLE);

  /* NVIC Configuration */
  NVIC_InitTypeDef NVIC_InitStructure;
  /* Enable the USARTx Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USARTy_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  /* Configure the GPIOs */
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Configure USARTy Tx (PA.09) as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = USARTy_TxPin;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);

  /* Configure USARTy Rx (PA.10) as input floating */
  GPIO_InitStructure.GPIO_Pin = USARTy_RxPin;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);

  /* Configure the USARTy */
  USART_InitTypeDef USART_InitStructure;
  USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl =
      USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_Init(USARTy, &USART_InitStructure);

  USART_Cmd(USARTy, ENABLE);
  USART_ITConfig(USARTy, USART_IT_RXNE, ENABLE);
}

void USARTy_IRQHandler(void) {
  if ((USARTy->SR & USART_FLAG_RXNE) != (u16)RESET) {
    RXc = USART_ReceiveData(USARTy);
    switch (RXc) {
    case '1':
      CMD_FLAG = 1;
      break;
    case '2':
      CMD_FLAG = 2;
      break;
    case 13:
      TIM_Cmd(TIM4, DISABLE);
      CMD_FLAG = 0;
      break;
    default:
      CMD_FLAG = 0;
      TIM_Cmd(TIM4, DISABLE);
      USARTSend("\nInput Number Errors!\n[*]>");
      break;
    }
  }
}
void TimerInit() {

  TIM_TimeBaseInitTypeDef TIMER_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

  TIM_TimeBaseStructInit(&TIMER_InitStructure);
  TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIMER_InitStructure.TIM_Prescaler = 7200; // 72000000/7200=10000
  TIMER_InitStructure.TIM_Period = 10000;   // 10000/10000=1s
  TIM_TimeBaseInit(TIM4, &TIMER_InitStructure);
  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
  TIM_Cmd(TIM4, DISABLE);

  /* NVIC Configuration */
  /* Enable the TIM4_IRQn Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
void (*TimeCallback)(void);
uint16_t TOut = 0;
void RxOK(void) { CMD_FLAG = 2; }
void RTimeout(void) { FLASHStatus = 0; }
void SetTimeOut(void (*TC)(void), uint16_t t) {
  TimeCallback = TC;
  TOut = t;
  TimeSec = 0;
  TIM_Cmd(TIM4, ENABLE);
}

void TIM4_IRQHandler(void) {
  if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) {
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    if ((TimeSec++) > TOut) {
      TIM_Cmd(TIM4, DISABLE);
      TimeCallback();
    }
  }
}
void FLASH_Init(void) { FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); }
int main(void) {
  // Initialize USART
  SystemInit();
  GPIO_LED_INIT();
  usart_init();
  LED(On);
  USARTSend(banner);
  TimerInit();
  SetTimeOut(RxOK, 3);
  while (1) {
    if (CMD_FLAG == 2) {
      TIM_Cmd(TIM4, DISABLE);
      LED(Off);
      USARTSend("[2]Boot UserApp..\n");
      LoadApp();
    }
    if (CMD_FLAG == 1) {
      TIM_Cmd(TIM4, DISABLE);
      FLASH_Init();
      USARTSend("[1]\n Ready To Update SW...\n");
      DMA_Configuration();
      uint8_t FLASH_Offset_tmp = 0;
      FLASHStatus = 1;
      while (FLASHStatus) {

        if (FLASH_Offset > FLASH_Offset_tmp) {
          SetTimeOut(RTimeout, 1);
          Programng(((FLASH_Offset - 1) * RX_BUF_SIZE), RX_BUF_SIZE,
                    (const uint8_t *)FwBuff);
          USARTSend(". ");
          FLASH_Offset_tmp = FLASH_Offset;
        }
      }
      Programng(FLASH_Offset * RX_BUF_SIZE, RX_BUF_SIZE,
                (const uint8_t *)FwBuff);
      CMD_FLAG = 0;
      USARTSend(" =>[Program Completed].");
      USARTSend("\n Boot UserApp..\n");
      LoadApp();
    }
  }
}

void Programng(uint16_t Offset, uint16_t SWSIZE, const uint8_t *DataBuff) {

  uint32_t BANK_END;
  uint32_t BANK_START;
  uint32_t EraseCounter = 0x00, Address = 0x00, addr_data = 0x00;
  uint32_t NbrOfPage = 0x00, Data = 0x00;

  BANK_START = Offset + BANK1_WRITE_START_ADDR;
  NbrOfPage = SWSIZE / FLASH_PAGE_SIZE;
  BANK_END = BANK_START + SWSIZE;
  FLASH_Unlock();

  FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
  for (EraseCounter = 0; (EraseCounter <= NbrOfPage); EraseCounter++) {
    FLASH_ErasePage(BANK_START + (FLASH_PAGE_SIZE * EraseCounter));
  }
  USARTSend(".");
  Address = BANK_START;
  while (Address < BANK_END) {
    /* Convert u8 to u32 then write to flash */
    Data = DataBuff[addr_data] | (DataBuff[addr_data + 1] << 8) |
           (DataBuff[addr_data + 2] << 16) | (DataBuff[addr_data + 3] << 24);
    FLASHStatus = FLASH_ProgramWord(Address, Data);
    Address = Address + 4;
    addr_data = addr_data + 4;
  }
  FLASH_LockBank1();
}

void DMA_Configuration(void) {
  // DMA 按照1k对齐
  DMA_InitTypeDef DMA_InitStructure;
  USART_ITConfig(USARTy, USART_IT_RXNE, DISABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  /* DMA1 channel5 Rx configuration */
  DMA_DeInit(DMA1_Channel5);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USARTy->DR));
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RX_BUF;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = RX_BUF_SIZE;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);

  // /* DMA1 channel4 Tx configuration */
  // DMA_DeInit(DMA1_Channel4);
  // DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USARTy->DR));
  // DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Send_Buffer;
  // DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  // DMA_InitStructure.DMA_BufferSize = ProgramSize;
  // DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  // DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  // DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  // DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  // DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  // DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  // DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  // DMA_Init(DMA1_Channel4, &DMA_InitStructure);

  DMA_Cmd(DMA1_Channel5, ENABLE);
  // DMA_Cmd(DMA1_Channel4, DISABLE);
  USART_DMACmd(USARTy, USART_DMAReq_Rx, ENABLE);
  // USART_DMACmd(USARTy, USART_DMAReq_Tx, ENABLE);
  DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
  NVIC_EnableIRQ(DMA1_Channel5_IRQn);
}

void DMA1_Channel5_IRQHandler(void) {
  DMA_ClearITPendingBit(DMA1_IT_TC5);
  // DMA_Cmd(DMA1_Channel5, DISABLE);
  FLASH_Offset++;
  memcpy(FwBuff, RX_BUF, RX_BUF_SIZE);
  USARTSend(".");
}
void ExitIAP(void) {
  TIM_Cmd(TIM4, DISABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, DISABLE);
  RCC_APB2PeriphClockCmd(USARTy_CLK | USARTy_GPIO_CLK, DISABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, DISABLE);
  DMA_Cmd(DMA1_Channel5, DISABLE);
  USART_Cmd(USARTy, DISABLE);
  USART_DMACmd(USARTy, USART_DMAReq_Rx, DISABLE);
  USART_ITConfig(USARTy, USART_IT_RXNE, DISABLE);
  DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, DISABLE);
  NVIC_DisableIRQ(DMA1_Channel5_IRQn);
  NVIC_DisableIRQ(TIM4_IRQn);
  NVIC_DisableIRQ(USARTy_IRQn);
  GPIO_LED_Destory();
}